package com.jaychan.oauth.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * @Author Jaychan tanjiecheng@borche.cn
 * @Date 2022/1/5 11:26
 */

@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    TokenStore tokenStore;

    @Autowired
    ClientDetailsService clientDetailsService;


    /**
     * 令牌断点安全约束配置，意思就是开放接口啦
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //  开启oauth/token_key验证端口权限访问
                .tokenKeyAccess("permitAll()")
                //  开启检查token的接口权限访问
                .checkTokenAccess("permitAll()")
                //  标识支持client_id和client_secret做登录认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置客户端详情，并不是所有客户端都能接入授权服务
     * 客户端详情配置，如秘钥，唯一id
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //客户端id
                .withClient("jaychan")
                //客户端秘钥
                .secret(new BCryptPasswordEncoder().encode("jaychan"))
                //资源id，比如一个订单服务作为一个资源，可以设置多个
                .resourceIds("res1")
                //授权模式，总共4种
                //refresh_token不是授权模式，配了这个表示支持令牌的刷新
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                //允许授权的范围
                .scopes("all")
                //false跳转到授权页面
                //是否需要授权，设置为true则不需要用户点击确认授权直接返回授权码
                .autoApprove(false)
                //重定向url
                .redirectUris("http://www.baidu.com");
    }

    /**
     * 令牌访问端点的配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authorizationCodeServices(authorizationCodeServices())
                .authenticationManager(authenticationManager)
                .tokenServices(tokenServices())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);

    }

    /**
     * 配置授权码模式所需要的服务
     * 使用授权码模式必须配置一个授权码服务，用来颁布和删除授权码，
     * 当然授权码也支持多种方式存储，比如内存，数据库，这里暂时使用内存方式存储
     * 授权模式的service，要使用授权码模式必须注入
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        //授权码暂时存在数据库中
        return new InMemoryAuthorizationCodeServices();
    }


    /**
     * 令牌服务的配置
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices services = new DefaultTokenServices();
        //客户端配置策略
        services.setClientDetailsService(clientDetailsService);
        //支持令牌刷新
        services.setSupportRefreshToken(true);
        //令牌存储服务（配置方式）
        services.setTokenStore(tokenStore);
        //access_token过期时间
        services.setAccessTokenValiditySeconds(60 * 60 * 2);
        //refresh_token过期时间
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
        return services;
    }


}
